package cn.jbolt.wechat.service;

import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import cn.jbolt.core.cache.JBoltGlobalConfigCache;
import cn.jbolt.core.common.enums.JBoltSystemLogTargetType;
import cn.jbolt.core.db.sql.Sql;
import cn.jbolt.core.enumutil.JBoltEnum;
import cn.jbolt.core.service.base.JBoltBaseService;
import cn.jbolt.wechat.cache.JBoltWechatConfigCache;
import cn.jbolt.wechat.common.WechatConfigKey;
import cn.jbolt.wechat.common.WechatMpinfoType;
import cn.jbolt.wechat.common.miniapp.WechatMaContext;
import cn.jbolt.wechat.common.mp.WechatMpContext;
import cn.jbolt.wechat.model.WechatConfig;
import cn.jbolt.wechat.model.WechatMpinfo;
import com.jfinal.aop.Inject;
import com.jfinal.kit.Kv;
import com.jfinal.kit.Okv;
import com.jfinal.kit.Ret;
import com.jfinal.kit.StrKit;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;

import java.util.ArrayList;
import java.util.List;

/**
 * WechatConfig Service
 *
 * @ClassName: JBoltWechatConfigService
 * @author: JFinal学院-小木 QQ：909854136
 * @date: 2020年5月2日
 */
public class JBoltWechatConfigService extends JBoltBaseService<WechatConfig> {
	public static final String FORMAT_XML = "XML";
	public static final String FORMAT_JSON = "JSON";
	protected WechatConfig dao = new WechatConfig().dao();
	@Inject
	protected JBoltWechatMpinfoService jboltWechatMpinfoService;

	@Override
	protected WechatConfig dao() {
		return dao;
	}

	/**
	 * 检测指定公众平台是否已经有配置了
	 *
	 * @param mpId
	 * @return
	 */
	public boolean checkWechatMpinfoInUse(Long mpId) {
		return exists("mp_id", mpId);
	}

	/**
	 * 根据配置类型 得到一个公众平台的配置
	 *
	 * @param mpId
	 * @param type
	 * @return
	 */
	public List<WechatConfig> getConfigList(Long mpId, int type) {
		return getCommonList(Okv.by("mp_id", mpId).set("type", type));
	}

	/**
	 * 得到一个公众平台的基础配置
	 *
	 * @param mpId
	 * @return
	 */
	public List<WechatConfig> getBaseConfigList(Long mpId) {
		return getConfigList(mpId, WechatConfig.TYPE_BASE);
	}

	/**
	 * 得到一个公众平台的微信支付配置
	 *
	 * @param mpId
	 * @return
	 */
	public List<WechatConfig> getPayConfigList(Long mpId) {
		return getConfigList(mpId, WechatConfig.TYPE_PAY);
	}

	/**
	 * 如果没有就初始化得到指定类型配置列表
	 *
	 * @param mpId
	 * @param type
	 * @return
	 */
	public List<WechatConfig> checkAndInitConfig(Long mpId, int type) {
		WechatMpinfo mpinfo = jboltWechatMpinfoService.findById(mpId);
		boolean isWxa = mpinfo.getType() == WechatMpinfoType.XCX.getValue();
		List<String> configKeys = getConfigKeyList(mpId, type);
		String[] keys = WechatConfigKey.getConfigKeys(type, isWxa);
		String[] names = WechatConfigKey.getConfigNames(type, isWxa);
		List<WechatConfig> configs;
		assert keys != null;
		if (configKeys == null || configKeys.size() == 0) {
			configs = initConfigs(mpId, keys, names, type);
		} else {
			processConfigs(mpId, configKeys, keys, names, type, isWxa);
			configs = getConfigList(mpId, type);
		}
		return configs;
	}

	/**
	 * 获取公众平台指定类型的配置keys
	 *
	 * @param mpId
	 * @param type
	 * @return
	 */
	private List<String> getConfigKeyList(Long mpId, int type) {
		return query(selectSql().select("config_key").eqQM("mp_id", "type").orderByIdAscIfPgSql(), mpId, type);
	}

	/**
	 * 已经设置的 和全部的对比出相差的数据后初始化
	 *
	 * @param mpId
	 * @param oldKeys
	 * @param keys
	 * @param names
	 * @param type
	 * @param isWxa
	 * @return
	 */
	private void processConfigs(Long mpId, List<String> oldKeys, String[] keys, String[] names, int type,
								boolean isWxa) {
		for (int i = 0; i < keys.length; i++) {
			processOneConfig(mpId, names[i], keys[i], type, null);
		}
		boolean existAppId = oldKeys.contains(WechatConfigKey.APP_ID);
		boolean existDomainUrl = oldKeys.contains(WechatConfigKey.SERVER_DOMAIN_URL);
		if (existAppId && existDomainUrl) {
			updateConfigServerDomainUrl(mpId, type, isWxa);
		}

	}

	/**
	 * 更新一个公众平台的服务器URL配置
	 *
	 * @param mpId
	 * @param type
	 * @param isWxa
	 */
	private void updateConfigServerDomainUrl(Long mpId, int type, boolean isWxa) {
		WechatConfig appIdConfig = findFirst(
				Okv.by("mp_id", mpId).set("type", type).set("config_key", WechatConfigKey.APP_ID));
		WechatConfig urlConfig = findFirst(
				Okv.by("mp_id", mpId).set("type", type).set("config_key", WechatConfigKey.SERVER_DOMAIN_URL));
		String url;
		if (isWxa) {
			url = JBoltGlobalConfigCache.me.getWechatWxaServerDomainRootUrl();
		} else {
			url = JBoltGlobalConfigCache.me.getWechatMpServerDomainRootUrl();
		}
		if (StrKit.notBlank(url) && StrKit.notBlank(appIdConfig.getConfigValue())) {
			String result = url + "?appId=" + appIdConfig.getConfigValue();
			if (!result.equals(urlConfig.getConfigValue())) {
				urlConfig.setConfigValue(result);
				urlConfig.update();
			}
		}

	}

	/**
	 * 初始化单个配置
	 *
	 * @param mpId
	 * @param name
	 * @param configKey
	 * @param type
	 * @param value
	 */
	private WechatConfig processOneConfig(Long mpId, String name, String configKey, int type, String value) {
		WechatConfig config = findFirst(Okv.by("mp_id", mpId).set("type", type).set("config_key", configKey));
		if (config == null) {
			config = new WechatConfig();
			config.setConfigKey(configKey);
			if (isOk(value)) {
				config.setConfigValue(value);
			}
			config.set("mp_id", mpId);
			config.setName(name);
			config.setType(type);
			config.save();
		} else {
			if (isOk(value)) {
				config.setConfigValue(value);
				config.update();
			}
		}
		return config;
	}

	/**
	 * 初始化一个公众平台的配置里的所有项目
	 *
	 * @param mpId
	 * @return
	 */
	private List<WechatConfig> initConfigs(Long mpId, String[] keys, String[] names, int type) {
		List<WechatConfig> configs = new ArrayList<WechatConfig>();
		WechatConfig config;
		for (int i = 0; i < keys.length; i++) {
			config = processOneConfig(mpId, names[i], keys[i], type, null);
			configs.add(config);
		}
		return configs;
	}

	/**
	 * 配置所有的公众平台 进入JFinal启动后的配置流程
	 */
	public void configAllEnable() {
		LOG.info("开始启动公众平台配置... start");
		List<WechatMpinfo> mps = jboltWechatMpinfoService.getAllEnableList();
		if (mps != null && mps.size() > 0) {
			for (WechatMpinfo mp : mps) {
				configOne(mp);
			}
		} else {
			LOG.warn("没有可用的公众平台配置");
		}
		LOG.info("所有公众平台启动完成... finish");
	}

	/**
	 * 启动单个
	 *
	 * @param mp
	 */
	public Ret configOne(WechatMpinfo mp) {
		LOG.info("正在启动公众平台:" + mp.getName());
		boolean isWxa = mp.getType() == WechatMpinfoType.XCX.getValue();
		Kv kv = getMpinfoConfig(mp.getId(), WechatConfig.TYPE_BASE);
		if (kv == null) {
			LOG.warn("公众平台:" + mp.getName() + "--基础配置异常");
			return fail("Warning:公众平台:" + mp.getName() + "--基础配置异常");
		}
		if (!kv.containsKey(WechatConfigKey.APP_ID)) {
			LOG.warn("公众平台:" + mp.getName() + "--基础配置-appId 异常");
			return fail("Warning:公众平台:" + mp.getName() + "--基础配置-appId 异常");
		}

		String app_id = kv.getStr(WechatConfigKey.APP_ID);
		if (StrKit.isBlank(app_id)) {
			LOG.info("公众平台:" + mp.getName() + "--基础配置-appId null");
			return fail("Warning:公众平台:" + mp.getName() + "--基础配置-appId 异常");
		}

		if (!kv.containsKey(WechatConfigKey.APP_SECRET)) {
			System.out.println("Warning:公众平台:" + mp.getName() + "--基础配置-appSecret 异常");
			return fail("Warning:公众平台:" + mp.getName() + "--基础配置-appSecret 异常");
		}

		String app_secret = kv.getStr(WechatConfigKey.APP_SECRET);
		if (StrKit.isBlank(app_secret)) {
			LOG.warn("公众平台:" + mp.getName() + "--基础配置-appSecret null");
			return fail("Warning:公众平台:" + mp.getName() + "--基础配置-appSecret 异常");
		}

		if (!kv.containsKey(WechatConfigKey.APP_GHID) && !isWxa) {
			LOG.warn("公众平台:" + mp.getName() + "--基础配置-原始ID  异常");
			return fail("Warning:公众平台:" + mp.getName() + "--基础配置-原始ID  异常");
		}
		String ghID = kv.getStr(WechatConfigKey.APP_GHID);
//		if(!isWxa) {
//			if(StrKit.isBlank(ghID)){
//				LOG.warn("公众平台:"+mp.getName()+"--基础配置-原始ID  null");
//				return fail("Warning:公众平台:"+mp.getName()+"--基础配置-原始ID  异常");
//			}
//		}
		if (StrKit.notBlank(ghID) && !ghID.contains("gh_")) {
			LOG.warn("公众平台:" + mp.getName() + "--基础配置-原始ID  的格式 异常");
			return fail("Warning:公众平台:" + mp.getName() + "--基础配置-原始ID 的格式 异常");
		}

		if (!isWxa) {
			if (!kv.containsKey(WechatConfigKey.SERVER_TOKEN)) {
				LOG.warn("公众平台:" + mp.getName() + "--基础配置-token 异常");
				return fail("Warning:公众平台:" + mp.getName() + "--基础配置-token 异常");
			} else {
				String server_token = kv.getStr(WechatConfigKey.SERVER_TOKEN);
				if (StrKit.isBlank(server_token)) {
					LOG.warn("公众平台:" + mp.getName() + "--基础配置-token null");
					return fail("Warning:公众平台:" + mp.getName() + "--基础配置-token 异常");
				}
			}
		}

		switch (JBoltEnum.getEnumObjectByValue(WechatMpinfoType.class, mp.getType())) {
			case FWH://服务号
			case DYH://订阅号
				configWxMp(kv);
				break;
			case XCX://小程序
				configWxMa(kv);
				break;
			case QYWX://企业号
				//TODO 待处理
				break;
		}
		LOG.info("公众平台:" + mp.getName() + "--启动完成");
		return SUCCESS;
	}

	public void configWxMa(Kv kv) {
		String appId = kv.getStr(WechatConfigKey.APP_ID);
		String appSecret = kv.getStr(WechatConfigKey.APP_SECRET);
		String token = kv.getStr(WechatConfigKey.SERVER_TOKEN);
		String dataFormat = kv.getStr(WechatConfigKey.SERVER_DATA_FORMAT);
		String aesKey = kv.getStr(WechatConfigKey.SERVER_ENCODINGAESKEY);
		WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
		config.setAppid(appId);
		config.setSecret(appSecret);
		config.setToken(token);
		config.setAesKey(aesKey);
		config.setMsgDataFormat(dataFormat);
		WechatMaContext.saveConfig(appId, config);

	}

	public void configWxMp(Kv config) {
		String appId = config.getStr(WechatConfigKey.APP_ID);
		String appSecret = config.getStr(WechatConfigKey.APP_SECRET);
		String token = config.getStr(WechatConfigKey.SERVER_TOKEN);
		String msgEncryptType = config.getStr(WechatConfigKey.SERVER_MSG_ENCRYPT_TYPE);
		String aesKey = config.getStr(WechatConfigKey.SERVER_ENCODINGAESKEY);


		WxMpDefaultConfigImpl mpConfig = new WxMpDefaultConfigImpl();

		mpConfig.setAppId(appId);
		mpConfig.setSecret(appSecret);
		mpConfig.setToken(token);
		mpConfig.setAesKey(aesKey);
		//TODO 这里不需要设置加解密方式吗？


		WechatMpContext.saveConfig(appId, mpConfig);

	}


	/**
	 * 获取一个公众平台的配置信息
	 *
	 * @param mpId
	 * @param type
	 * @return
	 */
	private Kv getMpinfoConfig(Long mpId, int type) {
		List<WechatConfig> configs = getConfigList(mpId, type);
		if (configs == null || configs.size() == 0) {
			return null;
		}
		Kv kv = Kv.create();
		for (WechatConfig config : configs) {
			kv.set(config.getConfigKey(), config.getConfigValue());
		}
		return kv;
	}

	/**
	 * 更新配置信息
	 *
	 * @param mpId
	 * @param type
	 * @param configs
	 * @return
	 */
	public Ret updateConfigs(Long mpId, int type, Boolean isWxa, List<WechatConfig> configs) {
		Ret checkRet = checkConfigs(mpId, type, configs);
		if (checkRet.isFail()) {
			return checkRet;
		}

		if (type == WechatConfig.TYPE_BASE) {
			updateConfigServerDomainUrl(mpId, type, isWxa);
			configs.forEach(config -> {
				// 清缓存
				JBoltWechatConfigCache.me.removeConfigValue(mpId, type, config.getConfigKey());
				if (config.getConfigKey().equals(WechatConfigKey.APP_ID)) {
					JBoltWechatConfigCache.me.removeMpidByAppId(config.getConfigValue());
					JBoltWechatConfigCache.me.removeAppIdConfig(config.getConfigValue());
				}
			});
		} else {
			// 清缓存
			configs.forEach(config -> JBoltWechatConfigCache.me.removeConfigValue(mpId, type, config.getConfigKey()));
		}
		batchUpdate(configs);
		return SUCCESS;
	}

	/**
	 * 检查配置项是否初始化和是否有重复相等数据
	 *
	 * @param mpId
	 * @param type
	 * @param configs
	 * @return
	 */
	private Ret checkConfigs(Long mpId, int type, List<WechatConfig> configs) {
		for (WechatConfig config : configs) {
			boolean exist = checkOneMpConfigExist(mpId, type, config.getConfigKey(), config.getId());
			if (!exist) {
				return fail("配置KEY初始化异常，请关闭窗口，重新打开");
			}
			if (type == WechatConfig.TYPE_BASE) {
				// 如果是基础配置 就得判断 APPID和 app_GHID全局不重复
				if (config.getConfigKey().equals(WechatConfigKey.APP_ID)) {
					boolean existAppId = checkAppIdExist(config.getConfigValue(), config.getId());
					if (existAppId) {
						return fail("系统内已经存在其它公众平台使用APPID:" + config.getConfigValue());
					}
				} else if (config.getConfigKey().equals(WechatConfigKey.APP_GHID)) {
					boolean existAppGhId = checkAppGhIdExist(config.getConfigValue(), config.getId());
					if (existAppGhId) {
						return fail("系统内已经存在其它公众平台使用原始ID:" + config.getConfigValue());
					}
				}
			}
		}
		return SUCCESS;
	}

	/**
	 * 检测配置值 排除指定ID值
	 *
	 * @param type
	 * @param configKey
	 * @param configValue
	 * @param excludeId
	 * @return
	 */
	private boolean checkConfigExist(int type, String configKey, String configValue, Long excludeId) {
		Sql sql = selectSql().selectId().eqQM("type", "config_key", "config_value").idNotEqQM().first();
		Integer existId = queryInt(sql, type, configKey, configValue, excludeId);
		return isOk(existId);
	}

	/**
	 * 检测是否有APPID重复的 排除指定ID值
	 *
	 * @param appId
	 * @param excludeId
	 * @return
	 */
	private boolean checkAppIdExist(String appId, Long excludeId) {
		return checkConfigExist(WechatConfig.TYPE_BASE, WechatConfigKey.APP_ID, appId, excludeId);
	}

	/**
	 * 检测是否有APP_GHID重复的 排除指定ID值
	 *
	 * @param appGhId
	 * @param excludeId
	 * @return
	 */
	private boolean checkAppGhIdExist(String appGhId, Long excludeId) {
		return checkConfigExist(WechatConfig.TYPE_BASE, WechatConfigKey.APP_GHID, appGhId, excludeId);
	}

	/**
	 * 查找指定mpId公众平台配置里 有没有与指定ID一致的配置
	 *
	 * @param mpId
	 * @param type
	 * @param configKey
	 * @param sameId
	 * @return
	 */
	public boolean checkOneMpConfigExist(Long mpId, int type, String configKey, Long sameId) {
		Sql sql = selectSql().selectId().eqQM("mp_id", "type", "config_key").idEqQM().first();
		Long existId = queryLong(sql, mpId, type, configKey, sameId);
		return isOk(existId);
	}

	/**
	 * 删除一个公众平台的配置信息
	 *
	 * @param mpId
	 * @return
	 */
	public Ret deleteByMpId(Long mpId) {
		return deleteBy(Okv.by("mp_id", mpId));
	}

	/**
	 * 删掉一个公众平台加载到APIconfig中的配置
	 *
	 * @param mpId
	 */
	public void removeOneEnableApiConfig(Long mpId) {
		WechatMpinfo mp = jboltWechatMpinfoService.findById(mpId);
		String appId = getWechatConfigAppId(mpId);
		switch (JBoltEnum.getEnumObjectByValue(WechatMpinfoType.class, mp.getType())) {
			case FWH://服务号
			case DYH://订阅号
				WechatMpContext.removeConfig(appId);
				break;
			case XCX://小程序
				WechatMaContext.removeConfig(appId);
				break;
			case QYWX://企业号
				//TODO 待处理
				break;
		}

	}

	/**
	 * 获取一个公众平台的APPID
	 *
	 * @param mpId
	 * @return
	 */
	public String getWechatConfigAppId(Long mpId) {
		return getWechatBaseConfigValue(mpId, WechatConfigKey.APP_ID);
	}

	/**
	 * 获取一个公众平台的APPSECRET
	 *
	 * @param mpId
	 * @return
	 */
	public String getWechatConfigAppSecret(Long mpId) {
		return getWechatBaseConfigValue(mpId, WechatConfigKey.APP_SECRET);
	}

	/**
	 * 获取一个公众平台的指定类型和key配置
	 *
	 * @param mpId
	 * @param type
	 * @param configKey
	 * @return
	 */
	public String getWechatConfigValue(Long mpId, int type, String configKey) {
		WechatConfig appIdConfig = findFirst(Okv.by("mp_id", mpId).set("type", type).set("config_key", configKey));
		return appIdConfig == null ? null : appIdConfig.getConfigValue();
	}

	/**
	 * 获取一个公众平台的指定基础配置
	 *
	 * @param mpId
	 * @param configKey
	 * @return
	 */
	public String getWechatBaseConfigValue(Long mpId, String configKey) {
		return getWechatConfigValue(mpId, WechatConfig.TYPE_BASE, configKey);
	}

	/**
	 * 获取一个公众平台的指定支付配置
	 *
	 * @param mpId
	 * @param configKey
	 * @return
	 */
	public String getWechatPayConfigValue(Long mpId, String configKey) {
		return getWechatConfigValue(mpId, WechatConfig.TYPE_PAY, configKey);
	}

	/**
	 * 获取一个公众平台的指定额外配置
	 *
	 * @param mpId
	 * @param configKey
	 * @return
	 */
	public String getWechatExtraConfigValue(Long mpId, String configKey) {
		return getWechatConfigValue(mpId, WechatConfig.TYPE_EXTRA, configKey);
	}

	/**
	 * 获取一个公众平台的Music_POST_MediaID
	 *
	 * @param mpId
	 * @return
	 */
	public String getWechatConfigMusicPostMediaId(Long mpId) {
		WechatConfig musicPostMediaId = findFirst(Okv.by("mp_id", mpId).set("type", WechatConfig.TYPE_BASE)
				.set("config_key", WechatConfigKey.MUSIC_POST_MEDIAID));
		return musicPostMediaId == null ? null : musicPostMediaId.getConfigValue();
	}

	/**
	 * 根据AppId值 获取配置
	 *
	 * @param appId
	 * @return
	 */
	public WechatConfig getAppIdConfig(String appId) {
		return findFirst(Okv.by("type", WechatConfig.TYPE_BASE).set("config_key", WechatConfigKey.APP_ID)
				.set(Okv.by("config_value", appId)));
	}

	/**
	 * 通过AppId获取mpId
	 *
	 * @param appId
	 * @return
	 */
	public Long getWechatMpidByAppId(String appId) {
		WechatConfig wechatConfig = getAppIdConfig(appId);
		if (wechatConfig == null) {
			return null;
		}
		return wechatConfig.getMpId();
	}

	@Override
	protected int systemLogTargetType() {
		return JBoltSystemLogTargetType.WECHAT_CONFIG.getValue();
	}

}
